package compiler;

import sun.rmi.runtime.NewThreadAction;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author tjc
 * @date 2020-3-19
 */
public class Parser {

    Lexer lexer;

    private Token curTok;

    private ASTNode tree;

    private Domain global = null;

    public static Map<String, VarType> typeMap = null;
    public static Map<String, OPType> opMap = new HashMap<String, OPType>(){{
        put("+", OPType.o_add);
        put("-", OPType.o_sub);
        put("=", OPType.o_assign);
        put("*", OPType.o_mul);
        put("/", OPType.o_div);
        put("%", OPType.o_mod);
        put(">", OPType.o_gt);
        put("<", OPType.o_lt);
        put("==", OPType.o_eq);
        put("!=", OPType.o_neq);
        put(">=", OPType.o_gteq);
        put("<=", OPType.o_lteq);
        put("!", OPType.o_not);
        put(",", OPType.o_comma);
        put(".", OPType.o_point);
        put("|", OPType.o_bor);
        put("^", OPType.o_xor);
        put("&", OPType.o_band);
        put("&&", OPType.o_and);
        put("||", OPType.o_or);
        put("(", OPType.o_zxkh);
        put(")", OPType.o_yxkh);
        put("[", OPType.o_zzkh);
        put("]", OPType.o_yzkh);
        put("{", OPType.o_zdkh);
        put("}", OPType.o_ydkh);
        put(">>", OPType.o_shr);
        put("<<", OPType.o_shl);
    }};

    static {
        typeMap = new HashMap<>();
        typeMap.put("int",VarType.INT);
        typeMap.put("byte",VarType.BYTE);
        typeMap.put("String",VarType.STRING);
        typeMap.put("char",VarType.CHAR);
        typeMap.put("double",VarType.DOUBLE);
        typeMap.put("float",VarType.FLOAT);
        typeMap.put("boolean", VarType.BIOOLEAN);
        typeMap.put("short",VarType.SHORT);
    }

    public Parser(Lexer lexer) {
        this.lexer = lexer;
        this.global = new Domain();
        opMap = new HashMap<>();
        opMap.put("+", OPType.o_add);
        opMap.put("-", OPType.o_sub);
        opMap.put("=", OPType.o_assign);
        opMap.put("*", OPType.o_mul);
        opMap.put("/", OPType.o_div);
        opMap.put("%", OPType.o_mod);
        opMap.put(">", OPType.o_gt);
        opMap.put("<", OPType.o_lt);
        opMap.put("==", OPType.o_eq);
        opMap.put("!=", OPType.o_neq);
        opMap.put(">=", OPType.o_gteq);
        opMap.put("<=", OPType.o_lteq);
        opMap.put("!", OPType.o_not);
        opMap.put(",", OPType.o_comma);
        opMap.put(".", OPType.o_point);
        opMap.put("|", OPType.o_bor);
        opMap.put("^", OPType.o_xor);
        opMap.put("&", OPType.o_band);
        opMap.put("&&", OPType.o_and);
        opMap.put("||", OPType.o_or);
        opMap.put("(", OPType.o_zxkh);
        opMap.put(")", OPType.o_yxkh);
        opMap.put("[", OPType.o_zzkh);
        opMap.put("]", OPType.o_yzkh);
        opMap.put("{", OPType.o_zdkh);
        opMap.put("}", OPType.o_ydkh);
        opMap.put(">>", OPType.o_shr);
        opMap.put("<<", OPType.o_shl);
    }

    public void startParse() {
        tree = parse();
        tree.printNode(0);
        
        startCheck(tree);
    }

    /**
     * 语义分析 类型检查
     * @param node
     */
    private void startCheck(ASTNode node) {

    }

    /**
     * 二元表达式类型检查
     * @param node
     */
    private void checkBinaryExpr(BinaryExpr node) {

    }


    public ASTNode parse() {
        curTok = nextToken();
        switch (curTok.type) {
            case TOKEN_ID:
            case TOKEN_INT:
            case TOKEN_STR:
                return parseExpr(new ASTNode(curTok));
            case TOKEN_OP:
                switch (curTok.value) {
                    case "++":
                        return parsePreUnaryExpr(curTok);
                    case "+":
                    case "*":
                    case "-":
                    case "/":
                    case "]":
                    case "[":
                    case ")":
                        error();
                    case "(":
                        return parseKH(curTok);
                    case "{":
                        return parseBlock(curTok);
                }
        }
        return null;
    }

    private ASTNode parseBlock(Token curTok) {
        BlockExpr block = new BlockExpr(curTok);

        while (true) {
            ASTNode cache = parse();
            if (cache == null) {
                return block;
            }
            block.addStmt(cache);
        }
    }

    /**
     * 解析表达式
     *
     * @param cur
     * @return
     */
    public ASTNode parseExpr(ASTNode cur) {
        Token next = nextToken();
        switch (next.type) {
            case TOKEN_ID:
                return new UnaryExpr(0, cur.token, parseExpr(new ASTNode(next)));
            case TOKEN_OP:
                switch (next.value) {
                    case "[":
                        return parseExpr(parseZZKH(cur, next));
                    case "]":
                        return cur;
                    case "+":
                    case "*":
                    case "-":
                    case "/":
                    case "=":
                    case ",":
                    case "==":
                        return parseBinaryExpr(cur, next);
                    case "(":
                        return parseCall(cur, next);
                    case ")":
                        return cur;
                    case "{":
                        return new StatementNodeSpecial(cur.token, parseBlockWithOutToken());
                    case "++":
                        return parseExpr(new UnaryExpr(1, next, cur));
                    case "}":
                        return null;

                }
            case TOKEN_BREAK:
                return cur;
            default:
                error();
        }
        return null;
    }

    private List<ASTNode> parseBlockWithOutToken() {
        List<ASTNode> StmtList = new ArrayList<>();
        while (true) {
            ASTNode cache = parse();
            if (cache == null) {
                return StmtList;
            }
            StmtList.add(cache);
        }
    }

    /**
     * 解析函数调用
     *
     * @param callName
     * @param op
     * @return
     */
    private ASTNode parseCall(ASTNode callName, Token op) {
        ASTNode tmp = parse();
        Token next = nextToken();
        switch (next.type) {
            case TOKEN_OP:
                switch (next.value) {
                    //TODO case分支直接复制过来了，还没修改
                    case "[":
                        return parseExpr(parseZZKH(new BinaryExpr(op, callName, tmp), next));
                    case "]":
                        return new BinaryExpr(op, callName, tmp);
                    case "+":
                    case "*":
                    case "-":
                    case "/":
                    case "=":
                    case ",":
                    case "==":
                        return parseBinaryExpr(new BinaryExpr(op, callName, tmp), next);
                    case "(":
                        return parseCall(new BinaryExpr(op, callName, tmp), next);
                    case ")":
                        return new BinaryExpr(op, callName, tmp);
                    case "{":
                        return new StatementNode(callName.token, tmp, parseBlock(next));
                    case "++":
                        return parseExpr(new UnaryExpr(1, next, new BinaryExpr(op, callName, tmp)));
                    case "}":
                        return null;

                }
            case TOKEN_BREAK:
                return new BinaryExpr(op, callName, tmp);
            default:
                error();
        }
        return new BinaryExpr(op, callName, tmp);
    }


    /**
     * 解析二元表达式
     *
     * @param pre 操作数1
     * @param cur 操作符
     * @return
     */
    public BinaryExpr parseBinaryExpr(ASTNode pre, Token cur) {
        //next 为操作数2
        Token next = nextToken();
        switch (next.type) {
            case TOKEN_OP:
                switch (next.value) {
                    case "(":
                        return (BinaryExpr) shake(cur, pre, parseKH(next));
                    default:
                        error();
                }
            case TOKEN_ID:
            case TOKEN_INT:
            case TOKEN_STR: {
                return (BinaryExpr) shake(cur, pre, parseExpr(new ASTNode(next)));
            }
            default:
                error();
        }
        return null;
    }

    /**
     * 解析数组
     *
     * @param cur 数组变量
     * @return
     */
    public ASTNode parseZZKH(ASTNode cur, Token op) {

        ASTNode cache = parse();
        return new BinaryExpr(op, cur, cache);
    }

    /**
     * @param op
     * @param pre
     * @param tmp
     * @return
     */
    public ASTNode shake(Token op, ASTNode pre, ASTNode tmp) {
        if (binaryOp(tmp.token.value)) {
            if (priority(op, tmp.token)) {
                ((BinaryExpr) tmp).left = new BinaryExpr(op, pre, ((BinaryExpr) tmp).left);
                return ((BinaryExpr) tmp);
            }
        }
        while ("(".equals(tmp.token.value)) {
            tmp = ((UnaryExpr) tmp).value;
        }
        while ("(".equals(pre.token.value)) {
            pre = ((UnaryExpr) pre).value;
        }
        return new BinaryExpr(op, pre, tmp);
    }

    public ASTNode unaryShake(Token op, ASTNode node) {
        if ("(".equals(node.token.value)) {
            node.token = op;
        }
        return node;
    }

    /**
     * 解析前缀一元表达式
     * ++x
     *
     * @param uop 一元操作符
     * @param
     * @return
     */
    private ASTNode parsePreUnaryExpr(Token uop) {
        ASTNode tmp = parse();
        if (tmp instanceof BinaryExpr) {
            ((BinaryExpr) tmp).left = new UnaryExpr(0, uop, ((BinaryExpr) tmp).left);
            return tmp;
        } else {
            return new UnaryExpr(0, uop, tmp);
        }
    }

    /**
     * 解析后缀一元表达式
     *
     * @param uop 一元操作符
     * @param
     * @return
     */
    private ASTNode parseAftUnaryExpr(Token uop, ASTNode node) {
        if ("(".equals(node.token.value)) {
            node.token = uop;
            return node;
        }
        //TODO 后缀表达式
        return new UnaryExpr(1, uop, node);
    }


    /**
     * 解析括号及括号后的表达式
     * 括号包装成一个一元表达式，和其它符号组成二元表达式时通过树的旋转调整优先级
     *
     * @param cur 左小括号
     * @return
     */
    private ASTNode parseKH(Token cur) {
        ASTNode cache = parse();
        Token next = nextToken(); //右括号后第一个token
        switch (next.type) {
            case TOKEN_OP:
                switch (next.value) {
                    case "++":
                        return parseAftUnaryExpr(next, new UnaryExpr(0, cur, cache));
                    case "+":
                    case "*":
                    case "-":
                    case "/":
                        return parseBinaryExpr(new UnaryExpr(0, cur, cache), next);
                    case ")":
                    case "]":
                        return new UnaryExpr(0, cur, cache);
                    default:
                        error();
                }
            case TOKEN_BREAK:
                return new UnaryExpr(0, cur, cache);
            case TOKEN_ID:
            case TOKEN_INT:
            case TOKEN_STR:
                cur.opType = OPType.o_cast;
                cur.value = "#";
                return shake(cur, cache, parseExpr(new ASTNode(next)));
            default:
                error();
        }
        return null;
    }

    /**
     * 比较优先级
     *
     * @param cur
     * @param token
     * @return
     */
    private boolean priority(Token cur, Token token) {
        if (getLevel(cur.value) > getLevel(token.value))
            return true;
        else
            return false;
    }

    private void error() {
        throw new RuntimeException();
    }

    private Token nextToken() {
        return lexer.nextToken();
    }

    private int getLevel(String op) {
        switch (op) {
            case "=":
                return 0;
            case "+":
            case "-":
                return 4;
            case "*":
            case "/":
                return 5;
            case "++":
                return 6;
            case "#":
                return 7;
            case "(":
                return 8;

        }
        return 0;
    }

    private boolean binaryOp(String op) {
        switch (op) {
            case "+":
            case "*":
            case "-":
            case "/":
            case "=":
            case "[":
                return true;
            default:
                return false;
        }
    }

    private boolean unaryOp(String op) {
        return !binaryOp(op);
    }

    public static void printSpace(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("| ");
        }
    }

    private static void printAST(ASTNode n) {
        n.printNode(0);
    }

}

class BlockExpr extends ASTNode {
    List<ASTNode> stmts;

    public BlockExpr(Token op) {
        super(op);
        stmts = new ArrayList<>();
    }

    public void addStmt(ASTNode stmt) {
        stmts.add(stmt);
    }

    public void printNode(int depth) {
        Parser.printSpace(depth);
        System.out.println("block {");

        for (ASTNode stmt : stmts) {
            stmt.printNode(depth + 1);
        }

        Parser.printSpace(depth);
        System.out.format("}\n");
    }

}

class BinaryExpr extends ASTNode {
    ASTNode left;
    ASTNode right;

    public BinaryExpr(Token token, ASTNode left, ASTNode right) {
        super(token);
        this.left = left;
        this.right = right;
    }


    @Override
    public void printNode(int depth) {
        Parser.printSpace(depth);
        System.out.format("binary {\n");

        Parser.printSpace(depth + 1);
        System.out.format("node: %s\n", token.print());

        Parser.printSpace(depth + 1);
        System.out.println("left:{");
        left.printNode(depth + 2);

        Parser.printSpace(depth + 1);
        System.out.print("}\n");

        Parser.printSpace(depth + 1);
        System.out.println("right:{");
        right.printNode(depth + 2);

        Parser.printSpace(depth + 1);
        System.out.print("}\n");

        Parser.printSpace(depth);
        System.out.format("}\n");
    }
}

class UnaryExpr extends ASTNode {
    ASTNode value;
    int type; // 0前缀  1后缀

    public UnaryExpr(ASTNode value) {
        this.value = value;
    }

    public UnaryExpr(int type, Token token, ASTNode value) {
        super(token);
        this.value = value;
        this.type = type;
    }


    @Override
    public void printNode(int depth) {
        Parser.printSpace(depth);
        System.out.format("unary {\n");

        Parser.printSpace(depth + 1);
        System.out.format("op %s \n", token.print());


        Parser.printSpace(depth + 1);
        System.out.format("node {\n");

        value.printNode(depth + 2);

        Parser.printSpace(depth + 1);
        System.out.format("}\n");


        Parser.printSpace(depth);
        System.out.format("}\n");
    }
}

class ArrayNode extends ASTNode {

    ASTNode name;
    ASTNode index;

    public ArrayNode(Token op, ASTNode name, ASTNode index) {
        super(op);
        this.name = name;
        this.index = index;
    }
}

class StatementNode extends ASTNode {
    ASTNode stmt;
    ASTNode block;

    public StatementNode(Token token, ASTNode stmt, ASTNode block) {
        super(token);
        this.stmt = stmt;
        this.block = block;
    }
}

class StatementNodeSpecial extends ASTNode {
    List<ASTNode> statements;

    public StatementNodeSpecial(Token t, List<ASTNode> statements) {
        super(t);
        this.statements = statements;
    }

    @Override
    public void printNode(int depth) {
        Parser.printSpace(depth);
        System.out.printf("statement");
        System.out.printf(" {\n");

        for (ASTNode stmt : statements) {
            stmt.printNode(depth + 1);
        }

        Parser.printSpace(depth);
        System.out.printf("}");

    }
}

class ASTNode {
    Token token;


    public ASTNode() {
    }

    public ASTNode(Token token) {
        this.token = token;
    }

    @Override
    public String toString() {
        return "ASTNode{" +
                "token=" + token +
                '}';
    }

    public void printNode(int depth) {
        Parser.printSpace(depth);
        System.out.print("node ");
        System.out.format("%s\n", token.print());
    }
}


class Variable {
    int size;
    //类型
    String type;
}

class Domain {
    public Domain parent, child;
    Map<String, Variable> vars;

    public Domain(Domain parent) {
        this.parent = parent;
        this.child = null;
        if (parent != null)
            parent.child = this;
        vars = new HashMap<>();
    }

    public Domain() {
        this.child = null;
        this.parent = this;
        vars = new HashMap<>();
    }
}

enum ASTType {
    //语法树类型
}